using System;
using Microsoft.SPOT;
using dotnetwarrior.NetMF.Game;
using Gadgeteer;

namespace RallyX
{
  class GamePlayScene : GameScene
  {
    private bool _loaded = false;
    private Maze _maze;
    private Font _font;
    private Bitmap _tileSet = Resources.GetBitmap(Resources.BitmapResources.Rally_X_2);

    private Sprite _gameOver;
    private Sprite _score100;
    private Sprite _score200;
    private Sprite _score300;
    private Sprite _score400;
    private Sprite _score500;
    private Sprite _score600;
    private Sprite _score700;
    private Sprite _score800;
    private Sprite _score900;
    private Sprite _score1000;
    private Sprite _lastScoreSprite;

    Player _player;
    Enemy[] _enemies;
    GameState _state;

    private int _highScore = 0;

    CountDownTimer _lastScoreTimer = new CountDownTimer(5000);
    CountDownTimer _gameStateTimer = new CountDownTimer(500);

    public override void LoadContent()
    {
      if (!_loaded)
      {
        _loaded = true;

        _font = Resources.GetFont(Resources.FontResources.small);
        
        _tileSet.MakeTransparent(Gadgeteer.Color.FromRGB(0, 255, 0));

        _maze = new Maze(_tileSet, 12, 12);
        _maze.GenerateLevel();
        _maze.MoveTo(16, 24);

        _player = new Player(_tileSet, _maze, 20, 50);

        _enemies = new Enemy[3];

        _enemies[0] = new Enemy(_tileSet, _maze, _player, 
          new Personality()
          {
            ScatterX = 5 << TileViewer.TileSizeShift,
            ScatterY = 0 << TileViewer.TileSizeShift,
            StartTileX = 18,
            StartTileY = 58,
            TargetOffsetX = 0,
            TargetOffsetY = 0
          });

        _enemies[1] = new Enemy(_tileSet, _maze, _player,
          new Personality()
          {
            ScatterX = 40 << TileViewer.TileSizeShift,
            ScatterY = 0 << TileViewer.TileSizeShift,
            StartTileX = 20,
            StartTileY = 58,
            TargetOffsetX = 32,
            TargetOffsetY = 32
          });

        _enemies[2] = new Enemy(_tileSet, _maze, _player,
          new Personality()
          {
            ScatterX = 40 << TileViewer.TileSizeShift,
            ScatterY = 64 << TileViewer.TileSizeShift,
            StartTileX = 22,
            StartTileY = 58,
            TargetOffsetX = -32,
            TargetOffsetY = -32
          });

        _gameOver = new Sprite(new AnimationSequence(_tileSet, 0, new Rect[] { new Rect(32, 72, 70, 8) }));
        _score100 = new Sprite(new AnimationSequence(_tileSet, 0, new Rect[] {new Rect(0, 80, 14, 8)}));
        _score200 = new Sprite(new AnimationSequence(_tileSet, 0, new Rect[] {new Rect(14, 80, 16, 8)}));
        _score300 = new Sprite(new AnimationSequence(_tileSet, 0, new Rect[] {new Rect(30, 80, 16, 8)}));
        _score400 = new Sprite(new AnimationSequence(_tileSet, 0, new Rect[] {new Rect(46, 80, 16, 8)}));
        _score500 = new Sprite(new AnimationSequence(_tileSet, 0, new Rect[] {new Rect(62, 80, 16, 8)}));
        _score600 = new Sprite(new AnimationSequence(_tileSet, 0, new Rect[] {new Rect(78, 80, 15, 8)}));
        _score700 = new Sprite(new AnimationSequence(_tileSet, 0, new Rect[] {new Rect(93, 80, 16, 8)}));
        _score800 = new Sprite(new AnimationSequence(_tileSet, 0, new Rect[] {new Rect(109, 80, 16, 8)}));
        _score900 = new Sprite(new AnimationSequence(_tileSet, 0, new Rect[] {new Rect(0, 88, 15, 8)}));
        _score1000 = new Sprite(new AnimationSequence(_tileSet, 0, new Rect[] { new Rect(15, 88, 19, 8) }));

        _gameOver.MoveTo(_maze.X + (6 * TileViewer.TileSize - 35), _maze.Y + (6 * TileViewer.TileSize - 4));
        AddToScene(_maze);

        _maze.TrackObject(_player);

        _maze.AddPlayer(_player);
        _maze.AddEnemy(_enemies[0]);
        _maze.AddEnemy(_enemies[1]);
        _maze.AddEnemy(_enemies[2]);

        _state = GameState.Playing;        
      }

      _lastScoreTimer.Expired += new EventHandler(LastScoreTimer_Expired);
      _gameStateTimer.Expired += new EventHandler(GameStateTimer_Expired);

      MessageService.Instance.Subscribe(typeof(Messages.AteFlagMessage), HandleAteFlagMessage);      
      MessageService.Instance.Subscribe(typeof(Messages.PlayerDiedMessage), HandlePlayerDiedMessage);
      base.LoadContent();
    }    

    void GameStateTimer_Expired(object sender, EventArgs e)
    {    
      if (_state == GameState.Dying)
      {
        _state = GameState.Playing;
        if (_player.Lives == 0)
        {
          _state = GameState.GameOver;
        }        
        _player.Enabled = true;
        for (int i = 0; i < _enemies.Length; i++)
        {
          _enemies[i].Enabled = true;
        }

        MessageService.Instance.Publish(Messages.ResetLevelMessage.Message());        
      }
    }

    void LastScoreTimer_Expired(object sender, EventArgs e)
    {
      if (_lastScoreSprite != null)
      {
        _lastScoreSprite.Owner = null;
      }
    }

    private void HandlePlayerDiedMessage(object message)
    {
      var playerDied = message as Messages.PlayerDiedMessage;
      _player.Enabled = false;
      for (int i = 0; i < _enemies.Length; i++)
      {
        _enemies[i].Enabled = false;
      }
      _state = GameState.Dying;
      _gameStateTimer.Start(500);
    }

    private void HandleAteFlagMessage(object message)
    {
      var ateFlag = message as Messages.AteFlagMessage;
      if (_lastScoreSprite != null)
      {
        _lastScoreSprite.Owner = null;
      }

      switch (ateFlag.Value)
      {
        case 100: _lastScoreSprite = _score100; break;
        case 200: _lastScoreSprite = _score200; break;
        case 300: _lastScoreSprite = _score300; break;
        case 400: _lastScoreSprite = _score400; break;
        case 500: _lastScoreSprite = _score500; break;
        case 600: _lastScoreSprite = _score600; break;
        case 700: _lastScoreSprite = _score700; break;
        case 800: _lastScoreSprite = _score800; break;
        case 900: _lastScoreSprite = _score900; break;
        case 1000: _lastScoreSprite = _score1000; break;
      }

      if (_lastScoreSprite != null)
      {
        _lastScoreSprite.MoveTo(ateFlag.TileX << TileViewer.TileSizeShift, ateFlag.TileY << TileViewer.TileSizeShift);
        _lastScoreSprite.Owner = _maze;
        _lastScoreTimer.Start();        
      }

      if (ateFlag.Count == 10)
      {
        Pause();
        _state = GameState.CountBonus;
      }
    }

    private void Pause()
    {
      _player.Enabled = false;
      foreach (var enemy in _enemies)
      {
        enemy.Active = false;
      }
    }

    private void Continue()
    {
      _player.Enabled = true;
      foreach (var enemy in _enemies)
      {
        enemy.Active = true;
      }
    }

    private void NewGame()
    {
      _player.NewGame();
      Reset();
    }

    private void NewLevel()
    {
      _maze.GenerateLevel();
      _player.NewLevel();
      Reset();
    }

    private void Reset()
    {
      _state = GameState.Playing;            
      for (int i = 0; i < _enemies.Length; i++)
      {
        _enemies[i].Reset();
      }
    }

    public override void Update(float elapsedTime)
    {
      if (_state == GameState.GameOver)
      {
        if (GameManager.Game.InputManager.Button1)
        {
          _maze.GenerateLevel();
          NewGame();
        }
      }
      else if (_state == GameState.CountBonus)      
      {
        if (_player.Fuel > 0)
        {
          _player.Fuel--;
          _player.Score += 100;
        }
        else
        {
          Continue();
          NewLevel();
        }
      }

      base.Update(elapsedTime);
    }

    public override void Draw()
    {
      Surface.Clear();
      base.Draw();
      UpdateHud();
      if (_state == GameState.GameOver)
      {
        _gameOver.Draw();
      }
    }

    private void UpdateHud()
    {
      Bitmap miniMap = _maze.GetMiniMap();

      int y = 16;
      int x = 320 - 16 - miniMap.Width;

      if (_player.Score > _highScore) _highScore = _player.Score;

      Surface.DrawText("HI-SCORE", _font, Color.White, 16, 2);
      Surface.DrawText(_highScore.ToString(), _font, Color.Red, 80, 2);

      Surface.DrawText("1UP", _font, Color.White, x, y);
      Surface.DrawText(_player.Score.ToString(), _font, Color.Cyan, x + 30, y);

      Surface.DrawText("FUEL", _font, Color.Green, x + 24, y + 18);

      int hudWidth = miniMap.Width;
      int halfHudWidth = hudWidth >> 1;

      Surface.DrawImage(x, 240 - 48 - miniMap.Height, miniMap, 0, 0, hudWidth, miniMap.Height);
      
      int fuelWidth = (int)((_player.Fuel / 100.0) * hudWidth);
      Surface.DrawImage(x, y + 15 + 15 + 9, _tileSet, 0, 96, 85, 2);

      Gadgeteer.Color fuelColor = _player.Fuel < 35 ? Gadgeteer.Color.Red : Gadgeteer.Color.Yellow;
      Surface.DrawRectangle(fuelColor, 0, x, y + 15 + 15, fuelWidth, 8, 0, 0, fuelColor, 0, 0, fuelColor, 0, 0, Bitmap.OpacityOpaque);

      y = 200;
      int lives = _player.Lives;
      for (int i = 0; i < lives; ++i)
      {
        Surface.DrawImage(x + (i << 4), y, _tileSet, 16, 64, 16, 16);
      }      
    }
  }

  enum GameState
  {    
    Playing,
    Dying,
    GameOver,
    CountBonus,
  }
}
